/* 
1.第一次初始化会给el添加preVnode属性，之后会通过判断preVnode属性是否存在，决定是diff判断，还是直接创建节点
  patch (el,vnode)
  patch (oldnode,vnode)
  2.如果preVnode存在
  3.判断isSameVnode,否，直接替换，是 --步骤4
  4.对比更新属性，比较儿子节点
  5.更新儿子子节
  6.children createEle创建元素

*/

import { isSameNode } from "./index.js";

export function patch(oldVnode, vnode) {

  const isRealElement = oldVnode.nodeType;
  if (isRealElement) { // 如果是初始化
    const elm = createEle(vnode) // 根据vnode创建新的节点
    // 替换掉老节点
    const parentNode = oldVnode.parentNode;
    parentNode.insertBefore(elm, oldVnode.nextSibling);// el.nextSibling 不存在就是null 如果为null insertBefore就是appendChild
    parentNode.removeChild(oldVnode);
    return elm // 返回最新节点
  } else {
    if (!isSameNode(vnode, oldVnode)) {
      return oldVnode.el.parentNode.replaceChild(createEle(vnode), oldVnode.el)
    }

    let el = vnode.el = oldVnode.el;
    // 如果是文本节点，直接更新，因为没有儿子
    if (!oldVnode.tag) {//文本节点
      if (vnode.text != oldVnode.text) {
        return el.textContent = vnode.text;
      }
    }

    // 相同节点，复用节点，在更新不一样的属性

    updateProperties(vnode, oldVnode.data) // 节点复用，属性更新

    // 比较儿子
    let oldChildren = oldVnode.children || [];
    let newChildren = vnode.children || [];

    //考虑特殊情况，直接处理，简化更新
    //1。老的有儿子，新的没有
    if (oldChildren.length > 0 && newChildren.length == 0) {
      el.interHtml = ''
    } else if (newChildren.length > 0 && oldChildren.length == 0) {
      //2。新的有儿子，老的没有
      newChildren.forEach(child => el.appendChild(createEle(child)))
    } else {
      //3新老都有儿子
      updateChildren(el, oldChildren, newChildren)
    }

  }


}

function updateChildren(el, oldChildren, newChildren) {
  // vue2 diff算法
  //vue内部先优化处理，实在不行，在暴力对比
  let oldStartIndex = 0
  let oldEndIndex = oldChildren.length - 1;
  let oldStartNode = oldChildren[0];
  let oldEndNode = oldChildren[oldEndIndex]


  let newStartIndex = 0
  let newEndIndex = newChildren.length - 1;
  let newStartNode = newChildren[0];
  let newEndNode = newChildren[newEndIndex]


  function makeKeyByIndex(children) {
    let map = {}
    children.forEach((item, index) => {
      map[children.key] = index
    })
    return map
  }
  let mapping = makeKeyByIndex(oldChildren)

  // diff 算法的复杂度是O(n) 比对的时候 指针交叉的时候 比对完成
  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    if (!oldStartNode) { // 在指针移动的时候 可能元素已经被移动走了，那就跳过这一项
      oldStartNode = oldChildren[++oldStartIndex]
    } else if (!oldEndNode) {
      oldEndNode = oldChildren[--oldEndIndex]
    } else if (isSameNode(newStartNode, oldStartNode)) {// 头头比较
      patch(oldStartNode, newStartNode) // 比较两个节点自身的差异，patch中也会递归他们的子节点
      oldStartNode = oldChildren[++oldStartIndex];
      newStartNode = newChildren[++newStartIndex]
    } else if (isSameNode(oldEndNode, newEndNode)) {// 尾尾比较
      patch(oldEndNode, newEndNode);
      oldEndNode = oldChildren[--oldEndIndex];
      newEndNode = newChildren[--newChildren];
    } else if (isSameNode(oldStartNode, newEndNode)) { // 头尾比较 
      // a b c d d c b a 
      // d c b a
      patch(oldStartNode, newEndNode);
      el.insertBefore(oldStartVnode.el, oldEndVnode.el.nextSibling)
      oldStartNode = oldChildren[++oldStartIndex];
      newEndNode = newChildren[--newEndIndex]
    } else if (isSameNode(oldEndNode, newStartNode)) {// 尾头比较
      // a  b c d c b a
      // a b c 
      patch(oldEndNode, newStartNode);
      el.insertBefore(oldEndNode.el, oldStartNode.el) // 将尾部插入到头部去
      oldEndNode = oldChildren[--oldEndIndex];
      newStartNode = newChildren[++newStartIndex]
    } else {
      // 之前都是考虑一些 用户的特殊情况，但是有些非特殊的，比如乱序
      // 乱序的优化逻辑，新节点新key值，直接创建新元素，新节点使用的key值，为老节点的，将新老 两个 key相同节点，的进行patch，
      // 在patch中进行属性更新或者创建新的element，尽量去优化
      let moveIndex = mapping[newStartNode.key];
      if (moveIndex == undefined) {
        // 没有直接将节点插入到开头的前面
        el.insertBefore(createEle(newStartNode), oldStartNode.el)
      } else {
        // 有的话需要复用
        // a undefined c d         e b    d1
        // e b d       
        let moveVnode = oldChildren[moveIndex]; // 找到复用的那个人，将他移动到前面去
        patch(moveVnode, newStartNode);
        el.insertBefore(moveVnode.el, oldStartNode.el);
        oldChildren[moveIndex] = undefined; // 将移动的节点标记为空
      }
      newStartNode = newChildren[++newStartIndex]
    }
  }

  if (newStartIndex <= newEndIndex) { // 新的多，将多的插入进去
    // 如果下一个是null 就是appendChild
    let anchor = newChildren[newEndIndex + 1] == null ? null : newChildren[newEndIndex + 1].el;
    for (let i = newStartIndex; i <= newChildren.length; i++) {
      el.insertBefore(createEle(newChildren[newStartIndex]), anchor)
    }
  }

  if (oldStartIndex <= oldEndIndex) {// 老的多，将多的删除
    for (let i = oldStartIndex; i <= oldEndIndex; i++) {
      let child = oldChildren[i];
      child && el.removeChild(child.el) // 父节点将对应的子节点删除
    }

  }

}
export function createEle(vnode) {
  let { tag, data, children, text, vm } = vnode;
  if (typeof tag === 'string') {
    vnode.el = document.createElement(tag);
    //如果data有属性，我们需要把data设置到元素上
    updateProperties(vnode);
    children.forEach(child => {
      vnode.el.appendChild(createEle(child))
    })
  } else {
    vnode.el = document.createTextNode(text)
  }
  return vnode.el;
}

function updateProperties(vnode, oldProps = {}) {
  // for (let key in props) {
  //   el.setAttribute(key, props[key]);
  // }

  let el = vnode.el;
  let newProps = vnode.data || {};
  let newStyle = newProps.style || {};
  let oldStyle = oldProps.style || {};

  //老的有样式，新的没有，就把样式覆盖掉
  for (let key in oldStyle) {
    if (!newStyle[key]) {
      el.style[key] = ''
    }
  }
  for (let key in newProps) { // 添加新属性，如果跟老属性相同会被覆盖掉
    if (key == 'style') {
      for (let key in newStyle) {
        el.style[key] = newStyle[key]
      }
    } else {
      el.setAttribute(key, newProps[key])
    }
  }
  for (let key in oldProps) { // 删除掉老的有，新的没有的属性
    if (!newProps[key]) {
      el.removeAttribute(key)
    }
  }
}